package controller

import (
	"fmt"
	"github.com/dtm-labs/dtm/dtmgrpc"
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"github.com/ifnk/micro-blog/internal/blog/dto/comment_dto"
	"github.com/ifnk/micro-blog/internal/blog/global"
	"github.com/ifnk/micro-blog/internal/blog/middleware"
	"github.com/ifnk/micro-blog/internal/pkg/base"
	response "github.com/ifnk/micro-blog/internal/pkg/http"
	"github.com/ifnk/micro-blog/internal/pkg/jwt"
	"github.com/ifnk/micro-blog/protobuf/comment_pb"
	"github.com/ifnk/micro-blog/protobuf/post_pb"
	"github.com/infobloxopen/atlas-app-toolkit/query"
	uuid "github.com/satori/go.uuid"
	"strconv"
	"strings"
)

func InitCommentController(r *gin.RouterGroup) {
	api := r.Group("/comment")
	api.GET("", getComments)
	api.GET("/:id", getComment)
	api.POST("listByPostId", listByPostId)
	// 这个 要 使用 jwt 和 casbin 中间件
	api.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) // 在这行之前的api 裸奔,之后的受保护
	api.POST("", createComment)
	api.PUT("/:id", updateComment)
	api.DELETE("/:id", deleteComment)
}

func listByPostId(c *gin.Context) {
	var input comment_dto.PostIdParams
	err := c.ShouldBindJSON(&input)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	res, err := global.CommentClient.GetListByPostId(c, &comment_pb.GetListByPostIdRequest{PostId: uint64(input.PostId)})
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	comments := res.Result
	var orms []comment_pb.CommentORM
	for _, comment := range comments {
		orm, err := comment.ToORM(c)
		if err != nil {
			response.FailWithMessage(err.Error(), c)
			return
		}
		orms = append(orms, orm)
	}
	response.OkWithData(orms, c)
}

func createComment(c *gin.Context) {
	var input comment_dto.Create
	err := c.ShouldBindJSON(&input)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	// 从header 头里面 获取 用户 信息
	token := c.Request.Header.Get("x-token")
	j := jwt.NewJwt(global.Logger, global.Conf)
	claims, err := j.ParseToken(token)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	comment := &comment_pb.Comment{
		Content: input.Content,
		UserId:  uint64(claims.ID),
		PostId:  input.PostId,
		Uuid:    uuid.NewV4().String(),
	}

	// 分布式事务(Saga 模式)
	dtmGRPCServerAddr := global.Conf.DTM.Server.Host + global.Conf.DTM.Server.GRPC.Port
	gid := dtmgrpc.MustGenGid(dtmGRPCServerAddr)
	global.Logger.Info("gid:", gid)
	saga := dtmgrpc.NewSagaGrpc(dtmGRPCServerAddr, gid).Add(
		global.Conf.Comment.Server.Host+global.Conf.Comment.Server.GRPC.Port+"/"+comment_pb.CommentService_ServiceDesc.ServiceName+"/Create",
		global.Conf.Comment.Server.Host+global.Conf.Comment.Server.GRPC.Port+"/"+comment_pb.CommentService_ServiceDesc.ServiceName+"/CreateCompensate",
		&comment_pb.CreateCommentRequest{
			Payload: comment,
		},
	).Add(
		global.Conf.Post.Server.Host+global.Conf.Post.Server.GRPC.Port+"/"+post_pb.PostService_ServiceDesc.ServiceName+"/IncrementCommentCount",
		global.Conf.Post.Server.Host+global.Conf.Post.Server.GRPC.Port+"/"+post_pb.PostService_ServiceDesc.ServiceName+"/IncrementCommentCountCompensate",
		&post_pb.IncrementCommentCountRequest{
			Id: input.PostId,
		},
	)
	saga.WaitResult = true
	err = saga.Submit()
	if err != nil {
		sprint := fmt.Sprintf("sage.submit 失败  -> %+v", err.Error())
		response.FailWithMessage(sprint, c)
		return
	}

	response.OkWithData(comment, c)
}

func getComment(c *gin.Context) {
	idStr := c.Param("id")
	id, _ := strconv.Atoi(strings.TrimSpace(idStr))
	res, err := global.CommentClient.Read(c, &comment_pb.ReadCommentRequest{Id: uint64(id)})
	orm, err := res.GetResult().ToORM(c)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	response.OkWithData(orm, c)
}
func deleteComment(c *gin.Context) {
	idStr := c.Param("id")
	id, _ := strconv.Atoi(strings.TrimSpace(idStr))
	res, err := global.CommentClient.Delete(c, &comment_pb.DeleteCommentRequest{Id: uint64(id)})
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	response.OkWithData(res, c)
}
func updateComment(c *gin.Context) {
	var input comment_pb.CommentORM
	idStr := c.Param("id")
	id, _ := strconv.Atoi(strings.TrimSpace(idStr))
	_ = c.ShouldBindJSON(&input)
	pb, _ := input.ToPB(c)
	pb.Id = uint64(id)
	res, err := global.CommentClient.Update(c, &comment_pb.UpdateCommentRequest{Payload: &pb})
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	orm, _ := res.GetResult().ToORM(c)
	response.OkWithData(orm, c)
}
func getComments(c *gin.Context) {
	var params base.PageEntity
	_ = c.ShouldBindQuery(&params)
	offset := int32(params.PageIndex-1) * int32(params.PageSize)
	limit := int32(params.PageSize)
	client := global.CommentClient
	color.Blue("client -> %+v", client)
	menuRes, err := global.CommentClient.List(c, &comment_pb.ListCommentRequest{Paging: &query.Pagination{Offset: offset, Limit: limit}})
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	global.Logger.Infof("menuRes -> %+v", menuRes)

	var pbResponse []comment_pb.CommentORM
	for _, item := range menuRes.GetResults() {
		orm, err := item.ToORM(c)
		if err != nil {
			response.FailWithMessage(err.Error(), c)
			return
		}
		pbResponse = append(pbResponse, orm)
	}
	var total int32
	if menuRes.PageInfo == nil {
		total = 0
	} else {
		total = menuRes.PageInfo.Size
	}

	response.OkWithData(CommentPageRes{
		Total: total,
		Items: pbResponse,
	}, c)
}

type CommentPageRes struct {
	Total int32
	Items []comment_pb.CommentORM
}
